﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Job.Core.Cache
{
    /*
     * 本地缓存类
     * 这是一个固定长度的双向链表，没有闭合，当然是起初没写成闭合的。
     * 怎么缓存的呢？ 就是比如 长度是3000，你缓存一篇文章 就放进去一个， 如果你从里面获取一个A，则A被提到链表的头部，再放入一个B，则B在头部， 
     * 其他的就会靠后排。如果缓存的数量超过的3000，再缓存新的时候会踢掉尾部的那个。因为那个用的次数很少。
     */
    public class CacheList<T> where T : class
    {
        public class Item
        {
            public string Key { get; set; }
            public T Value { get; set; }
            public Item Left { get; set; }
            public Item Right { get; set; }
            public DateTime LastTime { get; set; }

            public Item(string key, T value)
            {
                Key = key;
                Value = value;
            }
        }

        private readonly static object _locked = new object();
        private readonly IDictionary<string, Item> _dict;
        private Item _first;
        private Item _last;
        public int Length { get; private set; }

        /// <summary>
        /// 缓存最大时间 秒
        /// </summary>
        public int MaxTime { get; private set; }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="maxLength">缓存最大数量</param>
        /// <param name="maxTime">缓存最大时间 过期后移除 单位 分钟</param>
        public CacheList(int maxLength , int maxTime)
        {
            _dict = new Dictionary<string, Item>();
            Length = maxLength;
            MaxTime = maxTime;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        public bool HasKey(string key)
        {
            lock (_locked)
            {
                return _dict.ContainsKey(key);
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        public void Add(string key, T value)
        {
            var item = new Item(key, value);
            lock (_locked)
            {
                if (_dict.Count == 0)
                {
                    _last = _first = item;
                }
                else if (_dict.Count == 1)
                {
                    _last = _first;
                    _first = item;

                    _last.Left = _first;
                    _first.Right = _last;
                }
                else
                {
                    item.Right = _first;
                    _first.Left = item;
                    _first = item;
                }

                item.LastTime = DateTime.Now;

                if (_dict.Count >= Length)
                {
                    _last.Left.Right = null;
                    _dict.Remove(_last.Key);
                    _last = _last.Left;

                    //_dict.Remove(_last.Key);
                    //_last = _last.Left;
                    //_last.Right = null;
                }

                if (HasKey(key))
                {
                    _dict[key] = item;
                }
                else
                {
                    _dict.Add(key, item);
                }
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        public T Get(string key)
        {
            Item item = null;
            lock (_locked)
            {
                _dict.TryGetValue(key, out item);
            }
            if (item == null) return null;
            lock (_locked)
            {
                //缓存最大时间 过期后移除 单位 分钟
                if (item.LastTime.AddMinutes(MaxTime) < DateTime.Now)
                {
                    Remove(key);
                    return null;
                }

                if (_dict.Count == 1)
                {
                    return _first.Value;
                }

                if (item.Left != null)
                {
                    item.Left.Right = item.Right;
                }
                else
                {
                    return item.Value;
                }

                if (item.Right != null)
                {
                    item.Right.Left = item.Left;
                }
                else
                {
                    _last.Left.Right = null;
                    _last = _last.Left;
                }

                item.Left = null;
                item.Right = _first;
                _first.Left = item;
                _first = item;
            }
            return item.Value;
        }

        public List<T> GetList(string[] keys)
        {
            List<T> list = new List<T>();
            foreach (var item in keys)
            {
                T itemVal = Get(item);
                if (string.IsNullOrEmpty(item) || itemVal==null)
                    continue;
                if (list == null)
                    list = new List<T>();

                list.Add(itemVal);
            }
            return list;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="key"></param>
        /// <returns></returns>
        public void Remove(string key)
        {
            Item item = null;
            lock (_locked)
            {
                _dict.TryGetValue(key, out item);
            }
            if (item == null) return;

            lock (_locked)
            {
                if (item.Left != null)
                {
                    item.Left.Right = item.Right;
                }
                else
                {
                    _first = item.Right;
                }

                if (item.Right != null)
                {
                    item.Right.Left = item.Left;
                }
                else
                {
                    _last = item.Left;
                }

                _dict.Remove(key);
            }
        }

    }
    /*
    用法如下：
    public class ArticleCacheProvider
    {
      private static readonly CacheList<Article> CacheList;
 
      private static string GetCacheKey(string fileNameOrArticleId, int blogId)
      {
          return ("article_" + fileNameOrArticleId + "_" + blogId).ToLower();
      }
 
      static ArticleCacheProvider()
      {
          CacheList = new CacheList<Article>(3000);
      }
 
      public static Article Get(string fileNameOrArticleId, int blogId)
      {
          return CacheList.Get(GetCacheKey(fileNameOrArticleId, blogId));
      }
 
      public static void Set(Article entity)
      {
          //如果文章有别名，则存2份key，filename和articleId都可以获取缓存实体
          if (entity.FileName != entity.ArticleId.ToString())
          {
              CacheList.Add(GetCacheKey(entity.ArticleId.ToString(), entity.BlogId), entity);
          }
          CacheList.Add(GetCacheKey(entity.FileName, entity.BlogId), entity);
      }
 
      public static void Remove(string fileNameOrArticleId, int blogId)
      {
          CacheList.Remove(GetCacheKey(fileNameOrArticleId, blogId));
      }
 
      public static bool HasArticle(string fileNameOrArticleId, int blogId)
      {
          return CacheList.HasKey(GetCacheKey(fileNameOrArticleId, blogId));
      }
  } 
    */
}
